package com.cloudfun.campusshare.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cloudfun.campusshare.common.constant.AccountStatus;
import com.cloudfun.campusshare.common.constant.BusinessConstant;
import com.cloudfun.campusshare.common.exception.AuthenticationException;
import com.cloudfun.campusshare.common.model.AccessToken;
import com.cloudfun.campusshare.common.model.Token;
import com.cloudfun.campusshare.entity.AppEntity;
import com.cloudfun.campusshare.entity.UserAuthEntity;
import com.cloudfun.campusshare.entity.UserEntity;
import com.cloudfun.campusshare.repo.AppEntityRepo;
import com.cloudfun.campusshare.repo.UserEntityRepo;
import com.cloudfun.campusshare.service.TokenFactory;
import com.cloudfun.campusshare.service.UserAuthService;
import com.cloudfun.campusshare.util.JwtUtils;
import com.cloudfun.campusshare.util.TokenUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;

@Service
@Slf4j
public class TokenFactoryImpl implements TokenFactory {

    private static final String JWT_ISSUER = "hyperchain";
    public static final String DEFAULT_SECRETE = "hyperch@in";

    @Autowired
    private BusinessConstant businessConstant;
    @Autowired
    private UserEntityRepo userEntityRepo;
    @Autowired
    private AppEntityRepo appEntityRepo;
    @Autowired
    private UserAuthService userAuthService;


    @Override
    public String createJwtWithSecret(Token token, String secret) {

        if (StringUtils.isEmpty(secret)) {
            secret = DEFAULT_SECRETE;
        }

        String tokenEncryptStr = TokenUtil.encryptToken(token);
        long ttlMillis = businessConstant.TOKEN_VALID_TIME * 60000;

        String jwtStr = JwtUtils.createJWT(JWT_ISSUER, tokenEncryptStr, ttlMillis, secret);

        return jwtStr;
    }

    @Override
    public String createJwt(Token token, String pubKey) {
        String secret = userEntityRepo.findPWDByPubAddress(pubKey);
        if (StringUtils.isEmpty(secret)) {
            log.info("用户密码为空，采用默认密钥加密");
            secret = DEFAULT_SECRETE;
        }
        String jwtStr = createJwtWithSecret(token, secret);

        return jwtStr;
    }

    @Override
    public Token verifyJwt(String jwtStr) {
        if (StringUtils.isEmpty(jwtStr)) {
            String errMsg = "jwt为空";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }

        String tokenStr = getTokenStrWithoutSig(jwtStr);

        Token token = TokenUtil.decryptToken(tokenStr);

        if (ObjectUtils.isEmpty(token)) {
            String errMsg = "token为空";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }

        log.info("token解析结果：" + token);

        UserAuthEntity userAuthEntity = userAuthService.getByUserIdAndType(token.getUserId(), token.getIdentityType());
        if (ObjectUtils.isEmpty(userAuthEntity)) {
            String errMsg = "用户不存在";
            throw new AuthenticationException(errMsg);
        }

        UserEntity userEntity = userAuthEntity.getUser();
        if (userEntity.getStatus() == AccountStatus.INVALID.getCode()
                || userEntity.getStatus() == AccountStatus.FROZEN.getCode()
                || userEntity.getStatus() == AccountStatus.LOCK.getCode()) {
            String errMsg = "账户无效或冻结";
            throw new AuthenticationException(errMsg);
        }

        if (BooleanUtils.isNotTrue(userEntity.getEnable())) {
            String errMsg = "账户已被禁用";
            throw new AuthenticationException(errMsg);
        }

        String secret = userAuthEntity.getCredential();
        if (StringUtils.isEmpty(secret)) {
            log.info("用户密码为空，采用默认密钥加密");
            secret = DEFAULT_SECRETE;
        }

        // 验证签名
        JwtUtils.parseJWT(jwtStr, secret);
        return token;
    }

    @Override
    public String createAccessStrWithSecret(AccessToken accessToken, String secret) {

        String tokenEncryptStr = TokenUtil.encryptAccessToken(accessToken);
        long ttlMillis = businessConstant.TOKEN_VALID_TIME * 60000;

        String accessStr = JwtUtils.createJWT(JWT_ISSUER, tokenEncryptStr, ttlMillis, secret);

        return accessStr;
    }

    @Override
    public AccessToken verifyAccessStr(String accessStr) {
        if (StringUtils.isEmpty(accessStr)) {
            String errMsg = "accessStr为空";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }

        String tokenStr = getTokenStrWithoutSig(accessStr);

        AccessToken accessToken = TokenUtil.decryptAccessToken(tokenStr);

        if (ObjectUtils.isEmpty(accessToken)) {
            String errMsg = "access_token为空";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }
        log.info("access_token解析结果：" + accessToken);
        if (StringUtils.isEmpty(accessToken.getAppKey())) {
            throw new AuthenticationException("非法token");
        }
        AppEntity appEntity = appEntityRepo.findByAppKey(accessToken.getAppKey());
        if (null != appEntity) {
            // 验证签名
            JwtUtils.parseJWT(accessStr, appEntity.getSecretKey());
            return accessToken;
        }
        appEntity = appEntityRepo.findByProdAppKey(accessToken.getAppKey());
        if (null != appEntity) {
            // 验证签名
            JwtUtils.parseJWT(accessStr, appEntity.getProdSecretKey());
            return accessToken;
        }
        throw new AuthenticationException("非法token");
    }

    private static String getTokenStrWithoutSig(String jwt) {
        String[] arr = jwt.split("\\.");
        if (arr.length != 3 || StringUtils.isEmpty(arr[1])) {
            String errMsg = "jwt格式错误";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }

        String claimStr;

        try {
            claimStr = new String(Base64Utils.decodeFromString(arr[1]), "utf8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            String errMsg = "base64解码异常";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }

        JSONObject jsonObject = JSON.parseObject(claimStr);

        String result = jsonObject.getString(Claims.SUBJECT);

        if (StringUtils.isEmpty(result)) {
            String errMsg = "从jwt获取token加密串失败";
            log.info(errMsg);
            throw new AuthenticationException(errMsg);
        }
        return result;
    }

}
